////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "CircleHull.hpp"

#include <assert.h>
#include <fstream>
#include <iostream>

#include <SFML/OpenGL.hpp>

#include <SmegTools/Tools.hpp>

#include <SmegTools/Debug/DebugNew.hpp>

namespace smeg {
    namespace light {

////////////////////////////////////////////////////////////
/// Default Constructor
////////////////////////////////////////////////////////////
CircleHull::CircleHull( float _fRadius, 
                        unsigned _uNumVertices, 
                        const sf::Vector2f& _Center, 
                        float _fAngleDeg,
                        bool _bIsStatic,
                        ShadowCasterEntity* _pParent ) :
    Hull( _Center, _fAngleDeg, _bIsStatic, _pParent ),
    m_Radius( _fRadius, _fRadius ),
    m_bChangedNumVertices( true )
{
    SetNumVertices( _uNumVertices );
}
    
////////////////////////////////////////////////////////////
/// Constructor
////////////////////////////////////////////////////////////
CircleHull::CircleHull( const sf::Vector2f& _Radius, 
                        unsigned _uNumVertices, 
                        const sf::Vector2f& _Center, 
                        float _fAngleDeg,
                        bool _bIsStatic,
                        ShadowCasterEntity* _pParent ) :
    Hull( _Center, _fAngleDeg, _bIsStatic, _pParent ),
    m_Radius( _Radius ),
    m_bChangedNumVertices( true )
{
    SetNumVertices( _uNumVertices );
}
    
////////////////////////////////////////////////////////////
/// Copy Constructor
////////////////////////////////////////////////////////////
CircleHull::CircleHull( const CircleHull& _Copy ) :
    Hull( _Copy ),
    m_Radius( _Copy.m_Radius ),
    m_bChangedNumVertices( false )
{
}
    
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
CircleHull::~CircleHull()
{
}

////////////////////////////////////////////////////////////
/// Is the given point inside the hull
////////////////////////////////////////////////////////////
bool CircleHull::IsPointInHull( const sf::Vector2f& _Point ) const {
    if ( m_Radius.x == m_Radius.y ) {
        return math::SquaredLength( m_Center - _Point ) < m_Radius.x * m_Radius.x;
    } else {
        sf::Vector2f pNoAngle;
        float cosAngle = cos(-m_fAngleRad),
            sinAngle = sin(-m_fAngleRad);

        pNoAngle.x = ( cosAngle * _Point.x - sinAngle * _Point.y ) - m_Center.x;
        pNoAngle.y = ( sinAngle * _Point.x + cosAngle * _Point.y ) - m_Center.y;

        return ( ( pNoAngle.x * pNoAngle.x ) / ( m_Radius.x * m_Radius.x ) +
            ( pNoAngle.y * pNoAngle.y ) / ( m_Radius.y * m_Radius.y ) ) <= 1.0f;
    }
}

////////////////////////////////////////////////////////////
/// Compute the circle
////////////////////////////////////////////////////////////
void CircleHull::ComputeCircle() {
    float fInc = math::TWO_PI / (m_uNumVertices - 2);
    
    if (!m_pOriginalVertices || m_bChangedNumVertices) {
        delete[] m_pOriginalVertices;
        delete[] m_pVertices;
        m_pVertices = NULL;
        m_pOriginalVertices = new sf::Vector2f[m_uNumVertices];

        m_bChangedNumVertices = false;
    }

    unsigned i = 1;
    m_pOriginalVertices[0].x = 0.0f;
    m_pOriginalVertices[0].y = 0.0f;
    for ( float a = 0.0f; a <= math::TWO_PI && i < m_uNumVertices - 1; a += fInc, ++i ) {
        m_pOriginalVertices[i].x = m_Radius.x * cos( a );
        m_pOriginalVertices[i].y = m_Radius.y * sin( a );
    }

    m_pOriginalVertices[m_uNumVertices - 1].x = m_Radius.x * cos( math::TWO_PI );
    m_pOriginalVertices[m_uNumVertices - 1].y = m_Radius.y * sin( math::TWO_PI );

    ComputeHull();
}

////////////////////////////////////////////////////////////
/// Compute the AABB
////////////////////////////////////////////////////////////
void CircleHull::ComputeAABB() {
    if ( m_Radius.x == m_Radius.y ) {
        float fDiameter = m_Radius.x * 2.0f;
        m_AABB.Setup( m_Center, sf::Vector2f( fDiameter, fDiameter ) );
        TreeUpdate();
    } else {
        sf::Vector2f lowerBound( m_pVertices[0] ),
            upperBound( lowerBound );
        sf::Vector2f* pCurVert;

        for ( unsigned i = 0; i < m_uNumVertices; ++i ) {
            pCurVert = &m_pVertices[i];
        
            if ( pCurVert->x > upperBound.x )
                upperBound.x = pCurVert->x;
            if ( pCurVert->y > upperBound.y )
                upperBound.y = pCurVert->y;
            if ( pCurVert->x < lowerBound.x )
                lowerBound.x = pCurVert->x;
            if ( pCurVert->y < lowerBound.y )
                lowerBound.y = pCurVert->y;
        }
        m_AABB.SetBounds( lowerBound + m_Center, upperBound + m_Center );
    }
    TreeUpdate();
}

/*********************** Accessors ************************/
void CircleHull::SetRadius( float _fRadius, unsigned _uNumVertices ) {
    bool bCompute = false;
    if ( _fRadius != m_Radius.x && _fRadius != m_Radius.y ) {
        m_Radius.x = _fRadius;
        m_Radius.y = _fRadius;
        bCompute = true;
    }
    
    if ( _uNumVertices != m_uNumVertices ) {
        m_uNumVertices = _uNumVertices;
        m_bChangedNumVertices = true;
    }

    if ( bCompute || m_bChangedNumVertices ) {
        ComputeCircle();
    }
}

void CircleHull::SetRadius( const sf::Vector2f& _Radius, unsigned _uNumVertices ) {
    bool bCompute = false;
    if ( _Radius != m_Radius ) {
        m_Radius = _Radius;
        bCompute = true;
    }
    
    if ( _uNumVertices != m_uNumVertices ) {
        m_uNumVertices = _uNumVertices;
        m_bChangedNumVertices = true;
    }

    if ( bCompute || m_bChangedNumVertices ) {
        ComputeCircle();
    }
}

void CircleHull::SetNumVertices( unsigned _uNumVertices ) {
    if ( _uNumVertices != m_uNumVertices ) {
        m_uNumVertices = _uNumVertices;
        m_bChangedNumVertices = true;

        ComputeCircle();
    }
}

    } // namespace light
} // namespace smeg